# -*- coding: utf-8 -*-
"""
:mod:`nubio.core.nt`
====================

Provides classes to represent nucleotides, codons, nucleic acid sequences and 
alignments.

"""
from __future__ import (division, with_statement, absolute_import, generators,
                         nested_scopes)

from nubio.data.alphabet import (NT_IUPAC, GAP_IUPAC, NT_COMPLEMENT_IUPAC, GENETIC_CODE_STANDARD)
from nubox.char import (Char, CharVector, CharArray)
from nubio.core.aa import AaSeq

NT_DUMMY = (ord(GAP_IUPAC[0][0]),)
CODON_DUMMY = NT_DUMMY * 3


class Nt(Char):
    """
    A subclass of ``Char`` to represent a nucleotide.
    
    The class metadata can contain an ``ALPHABET`` and ``COMPLEMENT_CODE``
    that define the valid symbols and their complements respectively. The
    defaults are ``NT_COMPLEMENT_IUPAC`` and ``NT_IUPAC`` see: 
    ``nubio.data.alphabet``.
    
    """
    clsdummy = NT_DUMMY
    clsmeta = (
               ('COMPLEMENT_CODE', NT_COMPLEMENT_IUPAC),
               ('ALPHABET', NT_IUPAC),
               )

    def complement(self, code=None, inplace=False):
        """
        Returns the complement given a complement "code". Optionally modifies
        the object inplace.
        
        Arguments:
        
            - code(``dict``) [default: ``None``] the default complement code is
              ``NT_COMPLEMENT_IUPAC``.
            - inplace(``bool) [default: ``False``] if ``True`` the array will 
              be modified in-place.
        
        """
        code = code or self.meta['COMPLEMENT_CODE']
        try:
            return self.recode(code, inplace)
        except KeyError:
            raise ValueError("not all symbols have known complement")


class NtSeq(Nt, CharVector):
    """
    Represents a vector of nucleotides.
    
    """
    clschild = Nt


class NtAln(NtSeq, CharArray):
    """
    Represents a vector of nucleotides.
    
    """
    clschild = NtSeq


class Codon(Nt):
    """
    A subclass of ``Nt`` to represent a codon.
    
    The class metadata can contain an ``ALPHABET``, ``COMPLEMENT_CODE`` and
    ``GENETIC_CODE`` that define the valid symbols and their complements 
    respectively. The defaults are ``NT_COMPLEMENT_IUPAC``,  ``NT_IUPAC`` and
    ``GENETIC_CODE_STANDARD``. see: ``nubio.data.alphabet``.
    
    """
    clsdummy = CODON_DUMMY
    clsmeta = (
               ('ALPHABET', NT_IUPAC),
               ('COMPLEMENT_CODE', NT_COMPLEMENT_IUPAC),
               ('GENETIC_CODE', GENETIC_CODE_STANDARD),
               )

    def translate(self, code=None, strict=False, **kwargs):
        """
        Returns the translation given a genetic "code" in a ``AaSeq``. 
        
        Arguments:
        
            - code(``dict``) [default: ``None``] the default complement code is
              ``GENETIC_CODE_STANDARD``.
            - strict(``bool``) [default: ``False``] if ``True`` will require a
              valid start codon and terminate on stop codons.

        """
        code = code or self.meta['GENETIC_CODE']
        if not strict:
            data = list(range(int(len(self.data) / 3)))
            aaseq = AaSeq(data, **kwargs)
            for i, item in enumerate(self.iter_item()):
                try:
                    aaseq.set_item(i, code[item][0])
                except KeyError:
                    raise ValueError("not all codons have known translation")
        else:
            data = []
            for i, item in enumerate(self.iter_item()):
                if i == 0:
                    aa = code[item][1][0]
                    if aa == '-':
                        raise ValueError("invalid start codon")
                else:
                    aa = code[item][0][0]
                if aa == '*':
                    break
                data.append(aa)
            aaseq = AaSeq(data, **kwargs)
        return aaseq


class CodonSeq(Codon, NtSeq):
    """
    Represents a vector of codons.
    
    """
    clschild = Codon


class CodonAln(CodonSeq, NtAln):
    """
    Represents an alignment of codons.
    
    """
    clschild = CodonSeq

    def translate(self, code=None, strict=False, **kwargs):
        raise NotImplementedError


Nt.clsparent = NtSeq
NtSeq.clsparent = NtAln
Codon.clsparent = CodonSeq
CodonSeq.clsparent = CodonAln
